package c2.group.business.teampvp.sub;

import c2.group.business.base.GroupSubModule;
import c2.group.business.teampvp.TeamPvpInherit;
import c2.group.business.teampvp.TeamPvpModule;
import c2.group.business.teampvp.TeamPvpPlayer;
import cate.common.game.mail.MailCmd;
import cate.common.table.d.*;
import cate.common.table.pvp.teampvp.row.TeamPvpGradeRow;
import cate.common.table.pvp.teampvp.row.TeamPvpRankRow;
import cate.common.table.pvp.teampvp.row.TeamPvpRobotRow;
import cate.common.util.TimeTool;
import cate.game.play.part.PlaySide;
import cate.common.util.GameResult;
import cate.game.rank.po.Rank;
import cate.game.res.MixRes;
import cate.game.res.MixResItem;
import cate.game.role.RoleSimpleBase;
import cate.game.role.RoleSnapshot;
import cate.game.role.farm.TimeBasedPlayTimes;
import cate.game.util.IntervalCounter;
import com.google.common.collect.Lists;
import cp.solution.business.game.component.NoticeParam;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class TeamPvpPoolModule extends GroupSubModule<TeamPvpModule> {
    private Map<String, TeamPvpPlayer> players;
    private final SortedMap<Long, Map<String, TeamPvpPlayer>> robotScorePlayers = new TreeMap<>();
    private final Map<String, TeamPvpPlayer> namePlayers = new HashMap<>();
    private final SortedMap<Long, Map<String, TeamPvpPlayer>> powerPlayers = new TreeMap<>();
    private final TreeMap<Long, Map<String, TeamPvpPlayer>> scorePlayers = new TreeMap<>();

    @NoteField("继承的积分")
    private Map<String, TeamPvpInherit> inheritScores;

    private final Object poolLock = new Object();

    @Override
    public void initDependent() {
        this.inheritScores = useDb().teamPvp.getInherits();
        this.players = useDb().teamPvp.getPoolPlayers();
        for (TeamPvpPlayer player : players.values()) {
            if (player.fightTimes != null) {
                player.fightTimes.setup(GDTeamPvp.MAX_TIMES, GDTeamPvp.RECOVER_SECONDS * 1000L);
            }
            computeNewPlayer(player);
        }

        for(TeamPvpRobotRow robotRow : context.table.teamPvp.robot.list) {
            EcResult<PlaySide> psRes = context.play.sideBuilder.createPlaySideByCode(robotRow.instance);
            if(!psRes.ok()) {
                continue;
            }
            PlaySide ps = psRes.data;
            TeamPvpPlayer robotPlayer = new TeamPvpPlayer();
            robotPlayer.power = ps.getTotalPower();
            robotPlayer.score = robotRow.score;
            robotPlayer.ps = ps;
            Map<String, TeamPvpPlayer> groupScorePlayers = robotScorePlayers.computeIfAbsent(robotPlayer.score, k -> new HashMap<>());
            groupScorePlayers.put(robotPlayer.ps.snapshot.uid, robotPlayer);
        }
    }

    public void seasonReset() {
        synchronized (poolLock) {
            players.clear();
            namePlayers.clear();
            powerPlayers.clear();
            scorePlayers.clear();
        }
    }

    public List<Rank> loadGods() {
        List<Rank> rankList = new ArrayList<>();
        synchronized (poolLock) {
            for (Map<String, TeamPvpPlayer> players : scorePlayers.descendingMap().values()) {
                for (TeamPvpPlayer player : players.values()) {
                    if (player.challengeTimes <= 0) {
                        continue;
                    }
                    rankList.add(new Rank(
                            player.ps.snapshot,
                            player.score));
                    if (rankList.size() >= 3) {
                        return rankList;
                    }
                }
            }
        }
        return rankList;
    }

    public List<Rank> loadRanks() {
        List<Rank> rankList = new ArrayList<>();
        synchronized (poolLock) {
            for (Map<String, TeamPvpPlayer> players : scorePlayers.descendingMap().values()) {
                for (TeamPvpPlayer player : players.values()) {
                    if (player.challengeTimes <= 0) {
                        continue;
                    }
                    rankList.add(new Rank(
                            player.ps.snapshot,
                            player.score));
                    if (rankList.size() >= 100) {
                        return rankList;
                    }
                }
            }
        }
        return rankList;
    }

    public void seasonSettle(int season) {
        synchronized (poolLock) {
            int total = players.size();
            if (total == 0) {
                return;
            }
            List<Rank> ranks = new ArrayList<>();
            List<TeamPvpRankRow> rows = context.table.teamPvp.rank.getList();
            double index = 0;
            log.info("开始结算组队竞技排行榜，共{} 人", total);
            int meiwan = 0;
            int repeat = 0;
            Set<String> handlePlayers = new HashSet<>();
            for (Map<String, TeamPvpPlayer> players : scorePlayers.descendingMap().values()) {
                for (TeamPvpPlayer player : players.values()) {
                    String uid = player.ps.snapshot.uid;
                    if (player.challengeTimes <= 0) {
                        meiwan ++;
                        continue;
                    }
                    if (handlePlayers.contains(uid)) {
                        repeat ++;
                        continue;
                    }
                    handlePlayers.add(uid);
                    if (++ index <= 100) {
                        ranks.add(new Rank(
                                player.ps.snapshot.copy(),
                                player.score));
                    }
                    int srvId = player.ps.snapshot.base.srvId;
                    for (TeamPvpRankRow row : rows) {
                        if (index / total <= row.per) {
                            int starMax = Optional.ofNullable(context.help.getSnapshot(uid))
                                .map(snapshot -> snapshot.shareStarMax)
                                .orElse(0);
                            int extra = context.table.pvpRankPointExtra.base.getExtraParam(starMax);
                            MailCmd mail;
                            if (extra > 0) {
                                MixRes res = new MixRes(row.rewardStr);
                                long count = res.count(GDObj.Type.NUM, 13);
                                long addNum = count * extra / GD.W_10000;
                                MixResItem item = new MixResItem(GDObj.Type.NUM, 13, addNum);
                                res.addItem(item);
                                mail = new MailCmd()
                                        .withContent(row.mailIdEx,
                                                Lists.newArrayList(
                                                        new NoticeParam(starMax),
                                                        new NoticeParam((extra / 100.0) + "%"),
                                                        new NoticeParam(NoticeParam.NoticeType.MIX_RES, new MixRes().addItem(item))))
                                        .withReward(res.toItemsStr());
                            } else {
                                mail = new MailCmd()
                                        .withContent(row.mailId, null)
                                        .withReward(row.rewardStr);
                            }
                            mail.addRole(uid)
                                    .withSrvId(srvId)
                                    .withOpr(GDOperation.TEAM_PVP_RANK_RERARD);
                            log.info("srvId：{}， uid:{}， 积分:{}， index={} ", srvId,
                                    uid, player.score, index);
                            context.common.sendMail(mail);
                            break;
                        }
                    }
                    updateInheritScore(season, uid, player.score);
                }
            }
            parent.updateRanks(ranks);
            log.info("统计本次参与人数，共{} 人， {}人没玩，{}人重复", total, meiwan, repeat);
        }
    }

    private final IntervalCounter c = new IntervalCounter(3);
    @Override
    public void onInterval() {
        if (c.trigger()) {
            if (isChallengeOpen()) {
                long start = System.currentTimeMillis();
                synchronized (poolLock) {
                    for (TeamPvpPlayer player : players.values()) {
                        player.fightTimesInit();
                        player.fightTimes.tick();
                    }
                }
                long cost = System.currentTimeMillis() - start;
                if (cost > 1000) {
                    log.info("玩家池tick过久, 共{}个玩家, cost={} ms", players.size(), cost);
                }
            }
        }
    }

    public List<TeamPvpPlayer> findPlayersByUid(Collection<String> playerUids) {
        List<TeamPvpPlayer> teamPlayers = new ArrayList<>();
        synchronized (poolLock) {
            for (String playerUid : playerUids) {
                TeamPvpPlayer player = players.get(playerUid);
                if (player != null) {
                    teamPlayers.add(player);
                }
            }
        }
        return teamPlayers;
    }

    private void computeNewPlayer(TeamPvpPlayer player) {
        namePlayers.put(getPlayerName(player.ps.snapshot.base, player.abbr), player);
        computePowerPlayers(player);
        computeScorePlayers(player);
    }

    private void computePowerPlayers(TeamPvpPlayer player) {
        Map<String, TeamPvpPlayer> groupPowerPlayers = powerPlayers.computeIfAbsent(player.power, k -> new LinkedHashMap<>());
        groupPowerPlayers.put(player.ps.snapshot.uid, player);
    }

    private void computeScorePlayers(TeamPvpPlayer player) {
        Map<String, TeamPvpPlayer> groupScorePlayers = scorePlayers.computeIfAbsent(player.score, k -> new LinkedHashMap<>());
        groupScorePlayers.put(player.ps.snapshot.uid, player);
    }

    public List<TeamPvpPlayer> matchPowerList(String leaderUid) {
        synchronized (poolLock) {
            TeamPvpPlayer player = players.get(leaderUid);
            if (player == null) {
                return Collections.emptyList();
            }
            long begin = (long) (player.power * 0.8);
            long end = (long) (player.power * 1.2);
            List<TeamPvpPlayer> list = powerPlayers.subMap(begin, end).values().stream()
                    .flatMap(e -> e.values().stream())
                    .filter(e -> !StringUtils.equals(e.ps.snapshot.uid, leaderUid) && !parent.getTeam().hasTeam(e.ps.snapshot.uid) && !parent.getInvite().hasInvited(leaderUid, e.ps.snapshot.uid))
                    .collect(Collectors.toList());
            if (list.size() <= 15) {
                return list;
            }
            Collections.shuffle(list);
            return list.subList(0, 15);
        }
    }

    private List<TeamPvpPlayer> matchScoreList(long score, List<TeamPvpPlayer> players) {
        List<TeamPvpPlayer> matchPlayers = new ArrayList<>(players);
        matchPlayers.addAll(matchScoreList((long) (score * 0.6), (long) (score * 1.4), matchPlayers.stream().map(e -> e.ps.snapshot.uid).collect(Collectors.toList())));
        if (matchPlayers.size() < 6) {
            matchPlayers.addAll(matchScoreList((long) (score * 0.2), (long) (score * 1.8), matchPlayers.stream().map(e -> e.ps.snapshot.uid).collect(Collectors.toList())));
        }
        //添加机器人
        if (matchPlayers.size() < 6) {
            matchPlayers.addAll(matchRobotScoreList((long) (score * 0.6), (long) (score * 1.4), matchPlayers.stream().map(e -> e.ps.snapshot.uid).collect(Collectors.toList())));
        }
        if (matchPlayers.size() < 6) {
            matchPlayers.addAll(matchRobotScoreList((long) (score * 0.2), (long) (score * 1.8), matchPlayers.stream().map(e -> e.ps.snapshot.uid).collect(Collectors.toList())));
        }
        if (matchPlayers.size() < 6) {
            matchPlayers.addAll(forchMatchRobot(matchPlayers.stream().map(e -> e.ps.snapshot.uid).collect(Collectors.toList())));
        }
        return matchPlayers;
    }

    private List<TeamPvpPlayer> matchScoreList(long beginScore, long endScore, List<String> players) {
        //先这么写，上线后性能有问题就改粒度
        synchronized (poolLock) {
            List<TeamPvpPlayer> list = scorePlayers.subMap(beginScore, endScore).values().stream()
                    .flatMap(e -> e.values().stream())
                    .filter(e -> !players.contains(e.ps.snapshot.uid))
                    .collect(Collectors.toList());
            Collections.shuffle(list);
            if (list.size() <= 6 - players.size()) {
                return list;
            }
            return list.subList(0, 6 - players.size());
        }
    }

    private List<TeamPvpPlayer> matchRobotScoreList(long beginScore, long endScore, List<String> players) {
        //先这么写，上线后性能有问题就改粒度
        synchronized (poolLock) {
            List<TeamPvpPlayer> list = robotScorePlayers.subMap(beginScore, endScore).values().stream()
                    .flatMap(e -> e.values().stream())
                    .filter(e -> !players.contains(e.ps.snapshot.uid))
                    .collect(Collectors.toList());
            if (list.size() <= 6 - players.size()) {
                return list;
            }
            Collections.shuffle(list);
            return list.subList(0, 6 - players.size());
        }
    }

    private List<TeamPvpPlayer> forchMatchRobot(List<String> players) {
        //先这么写，上线后性能有问题就改粒度
        List<TeamPvpPlayer> list = new ArrayList<>();
        synchronized (poolLock) {
            for (Map<String, TeamPvpPlayer> player : robotScorePlayers.values()) {
                for (Map.Entry<String, TeamPvpPlayer> entry : player.entrySet()) {
                    if (list.size() >= 6 - players.size()) {
                        return list;
                    }
                    if (!players.contains(entry.getKey())) {
                        list.add(entry.getValue());
                    }
                }
            }
            return list;
        }
    }

    public TeamPvpPlayer searchPlayer(String name) {
        TeamPvpPlayer player;
        synchronized (poolLock) {
            player = namePlayers.get(name);
        }
        return player;
    }

    private boolean isChallengeOpen() {
        return TimeTool.isDayOfWeek(Calendar.SUNDAY) || TimeTool.isDayOfWeek(Calendar.SATURDAY);
    }

    public void updatePlayer(PlaySide ps, String abbr) {
        if (ps == null || ps.snapshot == null) {
            return;
        }
        RoleSnapshot snapShot = ps.snapshot;
        synchronized (poolLock) {
            TeamPvpPlayer player = players.get(snapShot.uid);
            if (player == null) {
                player = new TeamPvpPlayer();
                player.ps = ps;
                if (StringUtils.isNotBlank(abbr)) {
                    player.abbr = abbr;
                }
                player.score = initNewSeasonScore(parent.getSeason(), player.ps.snapshot.uid);
                player.power = ps.getTotalPower();
                if (isChallengeOpen()) {
                    player.fightTimesInit();
                }
                players.put(snapShot.uid, player);
                computeNewPlayer(player);
            } else {
                //更新最新的base
                String prePlayerName = getPlayerName(player.ps.snapshot.base, player.abbr);
                String playerName = getPlayerName(snapShot.base, abbr == null ? player.abbr : abbr);
                if (isChallengeOpen()) {
                    player.fightTimesInit();
                }
                player.ps = ps;
                if (StringUtils.isNotBlank(abbr)) {
                    player.abbr = abbr;
                }
                if (!StringUtils.equals(playerName, prePlayerName)) {
                    namePlayers.remove(prePlayerName);
                    namePlayers.put(playerName, player);
                }
                if (ps.getTotalPower() != player.power) {
                    Map<String, TeamPvpPlayer> oldPlayers = powerPlayers.get(player.power);
                    if (oldPlayers != null) {
                        oldPlayers.remove(player.ps.snapshot.uid);
                    }
                    player.power = ps.getTotalPower();
                    computePowerPlayers(player);
                }
                if (player.fightTimes != null) {
                    player.fightTimes.tick();
                }
            }
        }
    }

    public TeamPvpPlayer updatePlayer(String playerUid, long scoreChange) {
        synchronized (poolLock) {
            if (!players.containsKey(playerUid)) {
                return null;
            }
            TeamPvpPlayer player = players.get(playerUid);
            if (scoreChange != 0) {
                Map<String, TeamPvpPlayer> oldPlayers = scorePlayers.get(player.score);
                if (oldPlayers != null) {
                    oldPlayers.remove(player.ps.snapshot.uid);
                }
                player.score += scoreChange;
                player.score = Math.max(0, player.score);
                computeScorePlayers(player);
            }
            return player;
        }
    }

    private String getPlayerName(RoleSimpleBase base, String abbr) {
        return (abbr == null ? ("S" + base.srvId) : abbr) + "." + base.name;
    }

    public GameResult<TeamPvpPlayer> invite(String leaderUid, String inviteUid) {
        GameResult<TeamPvpPlayer> r = new GameResult<>();
        if (StringUtils.isBlank(leaderUid) || StringUtils.isBlank(inviteUid) || StringUtils.equals(leaderUid, inviteUid)) {
            return r.fail("邀请数据有误");
        }
        synchronized (poolLock) {
            if (!players.containsKey(leaderUid)) {
                return r.fail("您的数据未同步到匹配服务器，请重新登录一下");
            }
            if (!players.containsKey(inviteUid)) {
                return r.fail("您邀请的队友不存在噢");
            }
            TeamPvpPlayer invitePlayer = players.get(inviteUid);
            TeamPvpPlayer leaderPlayer = players.get(leaderUid);
            if (gradeGapWide(invitePlayer.score, leaderPlayer.score)) {
                return r.fail("玩家段位相差过大, 不能组队");
            }
            r.data = invitePlayer;
        }
        return r;
    }

    public GameResult<Void> agreeInvite(String inviteUid, String leaderUid) {
        GameResult<Void> r = new GameResult<>();
        synchronized (poolLock) {
            if (!players.containsKey(leaderUid)) {
                return r.fail("该玩家不存在噢");
            }
            if (!players.containsKey(inviteUid)) {
                return r.fail("您的数据未同步到匹配服务器，请重新登录一下");
            }
            TeamPvpPlayer invitePlayer = players.get(inviteUid);
            TeamPvpPlayer leaderPlayer = players.get(leaderUid);

            if (gradeGapWide(invitePlayer.score, leaderPlayer.score)) {
                return r.fail("玩家段位相差过大, 不能组队");
            }
        }
        return r;
    }

    private boolean gradeGapWide(long score, long score2) {
        int grade1 = 0;
        int grade2 = 0;
        TeamPvpGradeRow gradeRow = context.table.teamPvp.grade.findRow(score);
        if (gradeRow != null) {
            grade1 = gradeRow.id;
        }
        TeamPvpGradeRow gradeRow2 = context.table.teamPvp.grade.findRow(score2);
        if (gradeRow2 != null) {
            grade2 = gradeRow2.id;
        }
        return Math.abs(grade1 - grade2) > 2;
    }

    public GameResult<List<TeamPvpPlayer>> matchRefresh(List<String> matchPlayerUids) {
        GameResult<List<TeamPvpPlayer>> r = new GameResult<>();
        if (matchPlayerUids.isEmpty()) {
            return r.fail("没有人你匹配个锤子");
        }
        synchronized (poolLock) {
            long score = (long) matchPlayerUids.stream()
                    .mapToLong(e -> players.get(e) == null ? 1000 : players.get(e).score).average().orElse(1000);
            List<TeamPvpPlayer> matchPlayers = findPlayersByUid(matchPlayerUids);
            if (matchPlayers.size() != matchPlayerUids.size()) {
                return r.fail("您的队伍信息有误");
            }
            List<TeamPvpPlayer> matchScoreList = matchScoreList(score, matchPlayers);
            if (matchScoreList.size() < 6) {
                return r.fail("哎呀，您太强了，匹配不到对手噢");
            }
            r.data = matchScoreList;
        }
        return r;
    }

    public GameResult<List<TeamPvpPlayer>> matchOpponent(List<String> matchPlayerUids) {
        GameResult<List<TeamPvpPlayer>> r = new GameResult<>();
        if (matchPlayerUids.isEmpty()) {
            return r.fail("没有人你匹配个锤子");
        }
        synchronized (poolLock) {
            long score = (long) matchPlayerUids.stream()
                    .mapToLong(e -> players.get(e) == null ? 1000 : players.get(e).score).average().orElse(1000);
            List<TeamPvpPlayer> matchPlayers = findPlayersByUid(matchPlayerUids);
            if (matchPlayers.size() != matchPlayerUids.size()) {
                return r.fail("您的队伍信息有误");
            }
            boolean allHasChallengeTimes = matchPlayers.stream().allMatch(e -> e.fightTimes.available().ok());
            if (!allHasChallengeTimes) {
                return r.fail("您的队伍挑战次数不足噢");
            }
            List<TeamPvpPlayer> matchScoreList = matchScoreList(score, matchPlayers);
            if (matchScoreList.size() < 6) {
                return r.fail("哎呀，您太强了，匹配不到对手噢");
            }
            for (TeamPvpPlayer matchPlayer : matchPlayers) {
                matchPlayer.challengeOnce();
            }
            r.data = matchScoreList;
        }
        return r;
    }

    private void updateInheritScore(int season, String roleUid, long score) {
        TeamPvpInherit inheritScore = inheritScores.computeIfAbsent(roleUid, e -> new TeamPvpInherit());
        inheritScore.score = score;
        inheritScore.season = season;
        inheritScore.uid = roleUid;
    }

    private long initNewSeasonScore(int season, String roleUid) {
        long score = 1000;
        synchronized (poolLock) {
            TeamPvpInherit inheritScore = inheritScores.get(roleUid);
            if (inheritScore == null) {
                return score;
            }
            score = inheritScore.score;
            if (inheritScore.season < season) {
                for (int s = inheritScore.season; s < season; ++ s) {
                    TeamPvpGradeRow gradeRow = context.table.teamPvp.grade.findRow(score);
                    if (gradeRow != null) {
                        score *= gradeRow.descreaseScale;
                    }
                }
            }
            inheritScores.remove(roleUid);
            return score;
        }
    }

    @Override
    public void onShutdown() throws Exception {
        super.onShutdown();
        savePlayers();
        useDb().teamPvp.saveInherit(inheritScores.values());
    }

    public void savePlayers() {
        synchronized (poolLock) {
            useDb().teamPvp.savePlayers(players.values());
        }
    }

    public GameResult<Void> gmResetTimes(String playerUid) {
        GameResult<Void> r = new GameResult<>();
        synchronized (poolLock) {
            TeamPvpPlayer player = players.get(playerUid);
            if (player == null) {
                return r.fail("没有您的数据噢");
            }
            player.fightTimes = new TimeBasedPlayTimes(GDTeamPvp.MAX_TIMES);
            player.fightTimes.setup(GDTeamPvp.MAX_TIMES, GDTeamPvp.RECOVER_SECONDS * 1000L);
        }
        return r;
    }
}
